<?php
/**
 * Copyright (c) 2006-2007, Julien PORTALIER
 * 
 * Licensed under The LGPL License
 * Redistributions of files must retain the above copyright notice.
 * 
 * @copyright    Copyright (c) 2006-2007, Julien PORTALIER
 * @link         http://feather-cm.google.com/
 * @license      http://www.opensource.org/licenses/lgpl-license.php The LGPL License
 */

abstract class GenericCommentModel extends ActiveRecord
{
	public    $refererClass;
	public    $paranoia         = false;
	public    $recursiveProcess = Q_PROCESS_LITE;
	public    $defaultOrder     = 'created ASC';
	public    $linkFields;
	public    $refererTitleField;
	
	// internals
	
	function __get($class)
	{
		if ($class == 'referer')
		{
			if (empty($this->refererClass))
				trigger_error("{$this->name}::refererClass must be defined.", E_USER_ERROR);
			return $this->referer = $this->{$this->refererClass};
		}
		return parent::__get($class);
	}
	
	protected function _getFields()
	{
		$fields = $this->linkFields ? $this->linkFields :
#			"{$this->name}.id, {$this->name}.created, {$this->name}.member_id, {$this->name}.user_name, {$this->name}.user_url, {$this->name}.user_url, {$this->name}.user_email, {$this->name}.body_html, {$this->name}.spam, {$this->name}.troll";
			"{$this->name}.*";
		if (isset($this->referer->linkFields))
			$fields .= ", ".$this->referer->linkFields;
		return $fields;
	}
	
	
	// read
	
	function & getAll($referer_id=null, $order='ASC', $limit=null, $page=null)
	{
		if (!is_null($referer_id))
			$this->condition("{$this->name}.{$this->referer->foreignKey}", $referer_id);
		$this->order("{$this->name}.created $order");
		return $this->findAll($this->_getFields(), $limit, $page, 0);
	}
	
	function & getLatest($limit=20, $referer_id=null)
	{
		if ($referer_id) {
			$this->condition("{$this->name}.{$this->referer->foreignKey}", $referer_id);
		}
		
		# no spam nor troll
		$this->conditions(array(
			"{$this->name}.spam"  => 0,
			"{$this->name}.troll" => 0,
		));
		
		$this->order("{$this->name}.created DESC");
		$this->limit($limit);
		return $this->findAll($this->_getFields(), null, null, 0);
	}
	
	
	// validate
	
	function beforeValidate()
	{
		# generates HTML from syntax
		if (isset($this->data[$this->name]['body']))
		{
			Load::libs('markdown_syntax');
			$MarkdownSyntax = new MarkdownSyntax;
			$this->data[$this->name]['body_html'] = $MarkdownSyntax->transform($this->data[$this->name]['body']);
		}
		
		# validation rules
		if (empty($this->validate))
		{
			$this->validate = array(
				'body' => array(
					Valid::NotEmpty => _('You must say something.'),
					'checkBody'     => _("BBcode markup isn't allowed."),
					'checkBodyHTML' => _("HTML links aren't allowed.")
				),
				'user_name'  => array('checkUserName' => _("You ain't an URL, are you?")),
#				'user_email' => array(
#					Valid::NotEmpty => _('Please enter your email.'),
#					'checkEmail'    => _('This email is invalid.')
#				)
			);
			
			# paranoia mode?
			if ($this->paranoia) {
				$this->validate['body']['checkBodyLinks'] = _("Too many links for not much words! Are you a spammer?");
			}
		}
		return true;
	}
	
	function troll_counter($user_ip=null, $time='-1 day')
	{
	  $this->conditions(array(
	    'user_ip' => ($user_ip === null) ? $this->get_user_ip() : $user_ip,
	    'created' => '> '.Time::SQL($time),
    ));
	  return $this->field('-! COUNT(*)');
	}
	
	function get_user_ip()
	{
	  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
	    return $_SERVER['HTTP_X_FORWARDED_FOR'];
	  }
	  elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
	    return $_SERVER['HTTP_CLIENT_IP'];
	  }
	  else {
	    return $_SERVER['REMOTE_ADDR'];
	  }
	}
  
	/**
	 * blog      (http://DOMAIN/blog/)
	 * permalink (http://DOMAIN/blog/posts/show/$id)
	 * comment_type (comment, trackback, pingback, registration, ...)
	 * comment_author
	 * comment_author_email
	 * comment_author_url
	 * comment_content
	 * article_date (post date)
	 */
	function afterValidate()
	{
		if ($this->paranoia and isset($this->typepad_antispam_api_key))
		{
			$this->load->behavior('TypePadAntiSpam', $this);
			$data = array(
				'blog'                 => $this->data[$this->name]['base_url'],
				'permalink'            => $this->data[$this->name]['permalink'],
				'comment_type'         => 'comment',
				'comment_author'       => $this->data[$this->name]['user_name'],
				'comment_author_email' => $this->data[$this->name]['user_email'],
				'comment_author_url'   => $this->data[$this->name]['user_url'],
				'comment_content'      => $this->data[$this->name]['body'],
				'article_date'         => preg_replace('/\D/', '', $this->data[$this->name]['article_date']),
			);
			if ($this->TypePadAntiSpam->check_comment(&$data, $this->typepad_antispam_api_key)) {
				$this->data[$this->name]['spam'] = true;
			}
		}
		return true;
	}
	
	# A username can't be an URL
	function checkUserName($field, $value)
	{
		return preg_match('#^(http|ftp)[s]?://.+#', $value);
	}
	
	# An email must be valid (if provided)
	/*
	function checkEmail($field, $value)
	{
		if (!empty($value))
		{
			return !preg_match(Valid::Email, $value);
		}
		return false;
	}
	*/
	
	# Body can't contain [url=...]...[/url]
	function checkBody($field, $value)
	{
		return preg_match('#\[url[=]?[^\]]*\][^\[]+\[/url\]#i', $value);
	}
	
	# Body can't contain <a href="...">
	function checkBodyHTML($field, $value)
	{
		return preg_match('#\<a[^>]*href=["\']?.+["\']?\>#i', $value);
	}
	
	# Let's check for SPAM:
	#   1. message cannot contain more than 20% of links compared to words;
	#   2. links can't take more space than text (speaking in chars).
	function checkBodyLinks($field, $value)
	{
		$tokens = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY);
		$links  = preg_grep('#((http|ftp|mailto)[s]?://[^\s]+?)#i', $tokens);
		$words  = array_diff($tokens, $links);
		
		# 1.
		$nb_words = count($words);
		$nb_links = count($links);
		
		# 2.
		$nb_chars       = strlen($value);
		$nb_chars_words = strlen(implode($words));
		$nb_chars_links = strlen(implode($links));
		/*
		debug(
			$tokens, $nb_words.' '.$nb_links,
			(100 / $nb_words * $nb_links).'%',
			$nb_chars.' '.$nb_chars_words.' '.$nb_chars_links,
			$nb_chars - $nb_chars_links,
			(100 / $nb_chars_words * $nb_chars_links).'%'
		);
		*/
		return (
			100 / $nb_words       * $nb_links       > 20  or
			100 / $nb_chars_words * $nb_chars_links > 100
		);
	}
	
	// create, update
	
	function beforeSave()
	{
		# preview? don't save!
		return !isset($this->params['form']['preview']);
	}
	
	function create()
	{
    $user_ip = $this->get_user_ip();
		$this->data[$this->name]['user_ip']   = $user_ip;
		$this->data[$this->name]['user_host'] = gethostbyaddr($user_ip);
		return $this->save();
	}
}
?>
